/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package server;

import db.DB;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.UnsupportedEncodingException;
import java.net.Proxy;
import java.net.ServerSocket;
import java.net.Socket;
import java.net.SocketException;
import java.security.NoSuchAlgorithmException;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import protocol.Message;
import protocol.MetaData;
import protocol.Protocol;
import utility.DemoMD5;
import utility.Utility;

/**
 *
 * @author Reinhard
 */
public class ServerThread extends Thread {

  private Socket socket = null;
  private ServerSocket serverSocket = null;
  private String username_ = null;
  public static File repodir = new File("D:\\8box\\repositori");
  private boolean loggedout = false;
  private BufferedOutputStream bos = null;
  private BufferedInputStream bis = null;

  public ServerThread(ServerSocket mServerSocket) {
    super("ServerThread");
    serverSocket = mServerSocket;
    try {
      this.socket = serverSocket.accept();
    } catch (IOException ex) {
      Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
    }
    System.out.println("New ServerThread created");
  }

  public int processLogin(String username, String password) throws SQLException, NoSuchAlgorithmException, NoSuchAlgorithmException, UnsupportedEncodingException {
    int ret = -1;
    DB db = new DB();
    db.executeQuery("SELECT * from user");
    ResultSet rs = db.getResultSet();
    while (rs.next()) {
//      System.out.println("username " + username);
//      System.out.println("db username " + DemoMD5.MD5(rs.getString("username")));
//      System.out.println("password " + password);
//      System.out.println("db password " + rs.getString("password"));
      if (DemoMD5.MD5(rs.getString("username")).equals(username) && rs.getString("password").equals(password)) {
        username_ = rs.getString("username");
        ret = rs.getInt("id_user");
      }
    }
    return ret;
  }

  /**
   * Checks if logged user already have repository folder at server,
   * if not, create it.
   * @param user_id 
   */
  public void initRepo(int id_user) throws SQLException {
    DB db = new DB();
    db.executeQuery("SELECT * FROM repo_server WHERE id_user=" + id_user);
    boolean existed = false;
    while (db.getResultSet().next()) {
      existed = true;
    }

    if (!existed) { // blom ada repo nich
      // ambil nama
      String nama = null;
      db.executeQuery("SELECT username FROM user WHERE id_user=" + id_user);
      while (db.getResultSet().next()) {
        nama = db.getResultSet().getString("username");
      }
      System.out.println("nama = " + nama);
      File f = new File(repodir.toString() + "\\" + nama);
      try {
        if (f.mkdir()) {
          System.out.println(f.toString() + " created.");
        } else {
          System.out.println(f.toString() + " failed to create.");
        }
      } catch (Exception ex) {
        ex.printStackTrace();
      }
//      System.out.println("INSERT INTO repo_server VALUES (" + id_user + ", '" + nama + "')");
      db.execute("INSERT INTO repo_server VALUES (" + id_user + ", '" + nama + "')");
    }
  }

  public void Downloading(Socket newSocket, long length, String pathDestination) throws FileNotFoundException, IOException {
    System.out.println("Start downloading");
    System.out.println("Tulis file ke : " + pathDestination);
    byte[] b = new byte[1024];
    int len = 0;
    long bytcount = 1024;
    FileOutputStream inFile = new FileOutputStream(pathDestination);
    InputStream is = newSocket.getInputStream();
    BufferedInputStream in2 = new BufferedInputStream(is, 1024);
    while ((len = in2.read(b, 0, 1024)) != -1) {
      System.out.println("byte count=" + bytcount);
      bytcount = bytcount + 1024;
      inFile.write(b, 0, len);
    }
    inFile.close();
//        is.close();
    System.out.println("Bytes Writen : " + bytcount + " in " + pathDestination);
    //bosSocket.write(Byte.parseByte("TEEST"));
    System.out.println(bytcount + ":" + length);
    if (bytcount < length) {
      //pathDestination = pathDestination.replace("/", "\\");
      //System.out.println("file gagal "+pathDestination);
      //Utility.DeleteFile(pathDestination);
//            File file = new File(pathDestination);
//            if (file.exists()) {
//                if(file.delete())
//                System.out.println("file deleted");
//                else 
//                    System.out.println("file failed to delete");
//            } else {
//                System.err.println(
//                    "I cannot find '" + file + "' ('" + file.getAbsolutePath() + "')");
//            }
    } else {
      System.out.println("file has been downloaded");
    }
  }

  public void SendAckToClient() throws UnsupportedEncodingException, IOException, NoSuchAlgorithmException {
    Message ackMessage = new Message(Message.MESSAGE_ACK, null);
    ackMessage.sendMessage(socket, bos, ackMessage);
  }

  @Override
  public void run() {

    try {
      bos = new BufferedOutputStream(socket.getOutputStream());
      bis = new BufferedInputStream(socket.getInputStream());
    } catch (IOException ex) {
      Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
    }

    int line;
    String inputLine, outputLine;
    Protocol pro = new Protocol();

    while (!loggedout) {
      try {
        byte[] msg = new byte[1024]; // pesan awal
        bis.read(msg, 0, msg.length);
        System.out.println("MSG[0] = " + msg[0]);
        switch (msg[0]) {
          case Message.MESSAGE_LOGIN: // login
            String message = Message.byteToString(msg).trim();
            String username = message.split(" ")[0];
            String password = message.split(" ")[1];
            System.out.println(username);
            int id_user = processLogin(username, password);
            if (id_user != - 1) {
              initRepo(id_user);
              bos.write("Login success!".getBytes());
            } else {
              bos.write("Login failed!".getBytes());
            }
            break;
          case Message.MESSAGE_METADATA:
            System.out.println("masuk send metadata");
            System.out.println("username=" + username_);
            System.out.println("repodir = " + repodir.toString() + "\\" + username_);
            ArrayList<MetaData> metadatas = Utility.getMetaDataFromFolder(repodir.toString() + "\\" + username_);
            int num = metadatas.size();
            System.out.println("number of metadatas " + num);

            String numString = String.valueOf(num);
            bos.write(numString.getBytes());
            bos.flush();

            boolean getACK = false;
            while (!getACK) {
              byte[] baca = new byte[10];
              bis.read(baca);
              System.out.println("baca = " + Message.byteToString(baca));
              if (Integer.parseInt(Message.byteToString(baca).trim()) == Message.MESSAGE_ACK) {
                getACK = true;
              }
            }

            for (int i = 0; i < metadatas.size(); ++i) {
              String toSend = MetaData.getMetaDataString(metadatas.get(i));
              bos.write(toSend.getBytes());
              bos.flush();
              getACK = false;
              while (!getACK) {
                byte[] baca = new byte[10];
                bis.read(baca);
                if (Integer.parseInt(Message.byteToString(baca).trim()) == Message.MESSAGE_ACK) {
                  getACK = true;
                }
              }
            }

            break;
          case Message.MESSAGE_DOWNLOAD_REQUEST: // download request
            String message2 = Message.byteToString(msg).trim();
            System.out.println("message " + message2);
            //bos.write("Download Request accepted".getBytes());
            String[] pattern = message2.split("\"");
            // sendfile
            //System.out.println("patt1="+pattern[1]);
            String fileName = pattern[1];
            System.out.println("file name " + fileName);
            //System.out.println("filename=" + fileName);
            //System.out.println("length=" + pattern[0]);
            int PortDownload = 7000;
            ServerSocket serverFinder = null;
            boolean portDownloadAvailable = false;
            while (!portDownloadAvailable) {
              try {
                serverFinder = new ServerSocket(PortDownload);
              } catch (IOException e) {
                ++PortDownload;
              } finally {
                portDownloadAvailable = true;
              }
            }
            bos.write(("" + PortDownload).getBytes());
            bos.flush();
            System.out.println("mengirim port " + PortDownload);
            downloadRespon(serverFinder, repodir + "\\" + username_ + "\\" + fileName);

            break;
          case Message.MESSAGE_METADATA_RESP:

            break;
          case Message.MESSAGE_UPLOAD:
            String ori = Message.byteToString(msg).trim();
            String fileNameRev = "";
            String fileNameUpload = "";
            System.out.println("ori = " + ori);

            int k = ori.length() - 2;
            while (ori.charAt(k) != '"') {
              fileNameRev += ori.charAt(k);
              --k;
            }

            for (int i = fileNameRev.length() - 1; i >= 0; --i) {
              fileNameUpload += fileNameRev.charAt(i);
            }

            int fileSize = Integer.parseInt(ori.split(" ")[0]);
            boolean isFolder = Integer.parseInt(ori.split(" ")[1]) == 1 ? true : false;

            int port = 5002;
            ServerSocket server = null;
            boolean portAvailable = false;
            while (!portAvailable) {
              try {
                server = new ServerSocket(port);
              } catch (IOException e) {
                ++port;
              } finally {
                portAvailable = true;
              }
            }

            String portString = String.valueOf(port);
            bos.write(portString.getBytes());
            bos.flush();
            System.out.println("Port sent");

            Socket socketUpload = server.accept();

            if (isFolder) {
              Utility.CreateNewDirectory(repodir.getAbsolutePath() + "\\" + username_ + "\\" + fileNameUpload);
            } else {
              Downloading(socketUpload, fileSize, repodir.getAbsolutePath() + "\\" + username_ + "\\" + fileNameUpload);
            }

            socketUpload.close();
            server.close();

            break;
          case Message.MESSAGE_DELETE:
            System.out.println("REQUEST DELETE");
            byte[] msgdel = new byte[1024];
            bis.read(msgdel);
            String nums = Message.byteToString(msgdel).trim();

            System.out.println("num = " + nums);
            SendAckToClient();

            ArrayList<MetaData> am = Utility.getMetaDataFromFolder(repodir.getAbsolutePath() + "\\" + username_);

            System.out.println("Mau masuk for");
            for (int i = 0; i < Integer.parseInt(nums); ++i) {
              byte[] metadatabyte = new byte[1024];
              bis.read(metadatabyte);

              SendAckToClient();
              System.out.println("kirim ACK ke client");

              String metadataString = Message.byteToString(metadatabyte).trim();
              System.out.println("metadataString = " + metadataString);
              MetaData clientmd = MetaData.readMetaDataString(metadataString);

              boolean ada = false;
              boolean isServerNewer = false;
              boolean isFolder1 = false;
              System.out.println("masuk perbandingan dengan metadata server");
              for (int j = 0; j < am.size(); ++j) {
                System.out.println("serverpath = " + am.get(j).getFilePath());
                System.out.println("clientpath = " + clientmd.getFilePath());
                if (am.get(j).getFilePath().equals(clientmd.getFilePath())) {
                  System.out.println("sama");
                  ada = true;
                  System.out.println(am.get(j).getLastModified() + " " + clientmd.getLastModified());
                  if (am.get(j).getLastModified() > clientmd.getLastModified()) {
                    isServerNewer = true;
                  }
                  if (am.get(j).getFileType() == 1) {
                    isFolder = true;
                  }
                }
              }
              if (ada) { // ada tuh file nya
                System.out.println("ada filenya");
                if (isServerNewer) {
                  // lebi baru yg di server coy
                  System.out.println("file server lebih baru");
                } else {
                  // delete filenya
                  System.out.println("Masuk delete");
                  if (isFolder1) {
                    Utility.DeleteDirectory(repodir + "\\" + username_ + "\\" + clientmd.getFilePath());
                  } else {
                    Utility.DeleteFile(repodir + "\\" + username_ + "\\" + clientmd.getFilePath());
                  }
                }
              } else { // di server jg ga ada, ya do nothing sadja lah
              }
            }
            break;
          case Message.MESSAGE_LOGOUT:
            System.out.println("user logout");
            loggedout = true;
            break;
          default:
          // hoho
        }
        bos.flush();
      } catch (SocketException e) {
        // do nothing
      } catch (Exception e) {
        e.printStackTrace();
      }
    }

    try {
      System.out.println("Penutupan Socket");
      bos.close();
      bis.close();
      socket.close();
    } catch (IOException ex) {
      Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
    }

  }

  public void downloadRespon(ServerSocket mDownloadSocket, String url) {
    System.out.println("download respon port, " + url);
    try {
      byte[] buf = new byte[1024];
      Socket newSocket = mDownloadSocket.accept();

      OutputStream os = newSocket.getOutputStream();
      BufferedOutputStream out = new BufferedOutputStream(os, 1024);
      byte[] msg = new byte[1024]; // pesan awal
      BufferedInputStream bis = new BufferedInputStream(newSocket.getInputStream());
      bis.read(msg, 0, msg.length);
      String message2 = Message.byteToString(msg).trim();
      System.out.println("message " + message2);
      //bos.write("Download Request accepted".getBytes());
      String[] pattern = message2.split("\"");
      // sendfile
      //System.out.println("patt1="+pattern[1]);
      String fileName = pattern[1];
      System.out.println("file name " + fileName);
      System.out.println("truly url = " + repodir + "\\" + username_ + "\\" + fileName);
      FileInputStream in = new FileInputStream(repodir + "\\" + username_ + "\\" + fileName);
      System.out.println(url);
      int i = 0;
      int bytecount = 1024;
      int counter = 0;
      while ((i = in.read(buf, 0, 1024)) != -1) {
        counter += 1;
        System.out.println("bytecount=" + bytecount);
        bytecount = bytecount + 1024;
        try{
            out.write(buf, 0, i);
            out.flush();
        }catch(SocketException ex){
            System.out.println("Exception : "+ex);
            break;
        }
      }
      newSocket.shutdownOutput(); /* important */
      System.out.println("END");
      in.close();
      mDownloadSocket.close();
      //            out.close();
      ////            
      //            if(socket.isOutputShutdown())
      //            socket.close();
      //        serverSocket = new ServerSocket(4444);
      //        socket = serverSocket.accept();
      //        socket = serverSocket.accept();

      //socket = (new ServerSocket(Utility.SERVER_PORT)).accept();
      //System.out.println("Bytes Sent :" + bytecount);      
    } catch (IOException ex) {
      Logger.getLogger(ServerThread.class.getName()).log(Level.SEVERE, null, ex);
    }
  }
}